﻿using System;
using System.Diagnostics;
using FluentParser.Expressions;

namespace FluentParser.Expressions.Rules.Decoration
{
    /// <summary>
    /// Decorates an expression by forcing an extra condition
    /// before a parsing is considered successful.
    /// </summary>
    /// <typeparam name="TResult">Type of the source and final result</typeparam>
    /// <example><code>
    /// var parseWord = ParseString.Regex("[A-Za-z]*");
    /// var parseLongWord = parseWord.Where(text => text.Length > 10);
    /// 
    /// parseWord.Parse("word")             --> "word"
    /// parseWord.Parse("verylongword")     --> "verylongword"
    /// parseLongWord.Parse("word")         --> failure
    /// parseLongWord.Parse("verylongword") --> "verylongword"
    /// </code></example>
    [DebuggerDisplay("[ConditionExpressionRule Source={Source.GetType().Name}]")]
    public class ConditionExpressionRule<TResult> : Expr<TResult> 
    {
        public Expr<TResult> Source { get; private set; }
        public Func<TResult, bool> Predicate { get; private set; }

        public ConditionExpressionRule(Expr<TResult> source, Func<TResult, bool> predicate)
        {
            Source = source;
            Predicate = predicate;
            TypeOperation = OperationType.Decoration;
        }

        public override ParsingResult<TResult> Parse(GrammarContext context, int index)
        {
            ParsingResult<TResult> result = Source.Parse(context, index);
            
            if (result != null && Predicate(result.Value))
                return result;

            return null;
        }

        public override void ParseNode(GrammarContext context, ParsingNodeResult node)
        {
            ParsingNodeResult childNode = node.AddChild(Source, context);

            if (childNode.IsSuccess && !Predicate((TResult)childNode.Value))
            {
                node.IsSuccess = true;
                node.Value = childNode.Value;
            }
        }
    }
}